﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Environment;
using System.Windows.Input;
using System.Windows.Forms;
using Kokomo.PeInspector.Settings;
using System.IO;
using System.Reflection;
using Kokomo.PeInspector.VisualStudioAdapter;
using System.Diagnostics;
using System.Threading;

namespace Kokomo.PeInspector.Reflection
{
	[ViewType(typeof(AssemblyExporterView))]
	class AssemblyExporterViewModel : DocumentViewModel
	{
		public AssemblyExporterViewModel(
			Assembly assembly,
			AssemblyIndex assemblyIndex,
			SourceExportSettings settings,
			IMetadataHost host
			)
		{
			if (assembly == null) throw new ArgumentNullException("assembly");
			if (assemblyIndex == null) throw new ArgumentNullException("assemblyIndex");
			if (settings == null) throw new ArgumentNullException("settings");
			if (host == null) throw new ArgumentNullException("host");

			this._assembly = assembly;
			this._assemblyIndex = assemblyIndex;
			this._settings = settings;
			this._host = host;

			this.Title = UiString.FromString(Messages.ExportTitle + " " + assembly.GetName().Name);

			this.IncludeResources = true;

			this.IsInputEnabled = true;

			// Required by SelectedProjectFormat (below)
			this._exportCommand = new DelegatedCommand(this.BeginExport, this.CanExport);

			this.ProjectFormats = new ProjectFormat[] {
				ProjectFormat.VisualCSharp2008,
				ProjectFormat.VisualCSharp2010
			};
			this.SelectedProjectFormat = ProjectFormat.VisualCSharp2010;

			this.BrowseOutputPathCommand = new DelegatedCommand(this.BrowseOutputPath);
			this.OpenProjectFileCommand = new ParameterizedCommand(this.OpenProjectFile);

			this.OutputDirectory = settings.DefaultOutputPath;
		}

		private Assembly _assembly;
		private AssemblyIndex _assemblyIndex;
		private SourceExportSettings _settings;
		private IMetadataHost _host;

		private string _outputDirectory;
		public string OutputDirectory
		{
			get { return this._outputDirectory; }
			set
			{
				this._outputDirectory = value;
				this.OnPropertyChanged("OutputDirectory");
				this._exportCommand.OnCanExecuteChanged();
			}
		}

		public ICommand BrowseOutputPathCommand { get; private set; }
		private void BrowseOutputPath()
		{
			FolderBrowserDialog dialog = new FolderBrowserDialog() { SelectedPath = this._settings.DefaultOutputPath };
			DialogResult result = dialog.ShowDialog();
			if (result != DialogResult.OK) return;

			this.OutputDirectory = dialog.SelectedPath;
		}

		public IEnumerable<ProjectFormat> ProjectFormats { get; private set; }

		private ProjectFormat _selectedProjectFormat;
		public ProjectFormat SelectedProjectFormat
		{
			get { return this._selectedProjectFormat; }
			set
			{
				this._selectedProjectFormat = value;
				this.OnPropertyChanged("SelectedProjectFormat");
				this._exportCommand.OnCanExecuteChanged();
			}
		}

		#region Options
		private bool _includeResources;
		public bool IncludeResources
		{
			get { return this._includeResources; }
			set
			{
				this._includeResources = value;
				this.OnPropertyChanged("IncludeResources");
			}
		}

		private bool _openProjectOnComplete;
		public bool OpenProjectOnComplete
		{
			get { return this._openProjectOnComplete; }
			set
			{
				this._openProjectOnComplete = value;
				this.OnPropertyChanged("OpenProjectOnComplete");
			}
		}
		#endregion

		private bool _isInputEnabled;
		public bool IsInputEnabled
		{
			get { return this._isInputEnabled; }
			set
			{
				this._isInputEnabled = value;
				this.OnPropertyChanged("IsInputEnabled");
			}
		}

		private DelegatedCommand _exportCommand;
		public ICommand ExportCommand { get { return this._exportCommand; } }
		private bool CanExport()
		{
			return
				(this.OutputDirectory != null)
				&& (this.SelectedProjectFormat != null)
				;
		}
		private void BeginExport()
		{
			this.IsInputEnabled = false;
			this.Result = null;
			new Thread(this.Export).Start();

		}
		private void Export()
		{
			try
			{
				ProgressInfo progress = new ProgressInfo();
				this.ExportProgress = progress;

				string path = this.OutputDirectory;
				this._settings.DefaultOutputPath = path;
				Directory.CreateDirectory(path);
				AssemblyExporter exporter = new AssemblyExporter(this._assembly, this._assemblyIndex.Types, this._host.AssemblyListHost.LoadContext, path)
				{
					AssemblyInfoImports = AssemblyAnalyzerEditor.AssemblyInfoNamespaceImports,
					SourceDocumentImports = AssemblyAnalyzerEditor.GetSourceDocumentNamespaceImports(this._assembly),
					IncludeResources = this.IncludeResources
				};

				CSharpProject project = this.SelectedProjectFormat.CreateProject();
				this.Result = exporter.Export(project, progress);

				if (this.OpenProjectOnComplete)
				{
					this.OpenProjectFile(this.Result.ProjectFilePath);
				}
			}
			catch (Exception ex)
			{
				// TODO: Display error document
			}
			finally
			{
				this.ExportProgress = null;
				this.IsInputEnabled = true;
			}
		}

		private ProgressInfo _exportProgress;
		public ProgressInfo ExportProgress
		{
			get { return this._exportProgress; }
			set
			{
				this._exportProgress = value;
				this.OnPropertyChanged("ExportProgress");
			}
		}

		public ICommand OpenProjectFileCommand { get; private set; }
		private void OpenProjectFile(object arg)
		{
			string fileName = arg as string;
			if (fileName == null) return;

			Process.Start(fileName);
		}

		private AssemblyExporterResult _result;
		public AssemblyExporterResult Result
		{
			get { return this._result; }
			private set
			{
				this._result = value;
				this.OnPropertyChanged("Result");

				if (
					(value != null)
					&& (value.Details != null)
					)
				{
					string outputDirectory = value.OutputDirectory;
					List<AssemblyExporterResultDetailViewModel> detailViewModels = new List<AssemblyExporterResultDetailViewModel>(value.Details.Count);
					for (int i = 0; i < value.Details.Count; i++)
					{
						AssemblyExporterResultDetail detail = value.Details[i];

						TypeExportResult typeExportResult = detail as TypeExportResult;
						if (typeExportResult != null)
						{
							MemberViewInfo memberViewInfo = MemberViewInfo.GetMemberViewInfo(typeExportResult.Type);
							AssemblyExporterResultDetailViewModel detailViewModel = new AssemblyExporterResultDetailViewModel(
								AssemblyAnalyzerEditor.MetadataIconProvider.GetImage(memberViewInfo.IconKey),
								memberViewInfo.LongHeading,
								this._host.OpenNewViewCommand,
								typeExportResult.Type,
								typeExportResult.RelativeFileName,
								Path.Combine(outputDirectory, typeExportResult.RelativeFileName)
								);
							detailViewModels.Add(detailViewModel);
						}
						else
						{
							ResourceExportResult resourceExportResult = detail as ResourceExportResult;
							if (resourceExportResult != null)
							{
								AssemblyExporterResultDetailViewModel detailViewModel = new AssemblyExporterResultDetailViewModel(
									null,
									resourceExportResult.ResourceName,
									null,
									null,
									resourceExportResult.RelativeFileName,
									Path.Combine(outputDirectory, resourceExportResult.RelativeFileName)
									);
								detailViewModels.Add(detailViewModel);
							}
						}
					}
					this.DetailViewModels = detailViewModels;
				}
			}
		}

		private IEnumerable<AssemblyExporterResultDetailViewModel> _detailViewModels;
		public IEnumerable<AssemblyExporterResultDetailViewModel> DetailViewModels
		{
			get { return this._detailViewModels; }
			set
			{
				this._detailViewModels = value;
				this.OnPropertyChanged("DetailViewModels");
			}
		}
	}
}
